home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / FILER / X-FILES.ZIP / 057 / !X-Files / c / chunks < prev    next >
Text File  |  1997-02-13  |  38KB  |  1,383 lines

  1. /* chunks.c */
  2.  
  3. /*#define NOCOMPACT*/
  4. /*#define ERASE*/
  5. /*#define NOCHUNKCACHE*/
  6.  
  7. #include "chunks.h"
  8. #include "debug.h"
  9. #include "swis.h"
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14.  
  15. #define _max(x, y) ((x) > (y) ? (x) : (y))
  16. #define _min(x, y) ((x) < (y) ? (x) : (y))
  17.  
  18. #define FREE 0x45455246
  19.  
  20. xFiles_list xFiles_openFiles;
  21.  
  22. static void xFiles_checkChunk(xFiles_info *pInfo, const xFiles_chunk *pChunk)
  23. {
  24.    unsigned maxChunk = pInfo->fileHeader.chunkTable.size / sizeof(xFiles_chunk);
  25.    
  26.    if (pChunk->usage == FREE)
  27.    {
  28.       ASSERT(pChunk->offset < maxChunk);
  29.       ASSERT(pChunk->size == FREE);
  30.       ASSERT(pChunk->allocSize == FREE);
  31.    }
  32.    else
  33.    {
  34.       if (pChunk->offset != xFiles_roundDown(pInfo, pChunk->offset))
  35.       {
  36.          TRACE("offset: %08x\n", pChunk->offset);
  37.       }
  38.  
  39.       /*ASSERT(pChunk->offset == (pChunk->offset & ~(pInfo->fileHeader.allocationUnit-1)));*/
  40.       ASSERT(pChunk->allocSize == (pChunk->allocSize & ~(pInfo->fileHeader.allocationUnit-1)));
  41.       ASSERT(pChunk->size <= pChunk->allocSize);
  42.    }
  43. }
  44.  
  45. unsigned xFiles_chunkAddress(xFiles_info *pInfo, unsigned cnkNum)
  46. {
  47.    ASSERT(pInfo->fileHeader.chunkTable.offset >= sizeof(xFiles_header));
  48.    ASSERT(cnkNum * sizeof(xFiles_chunk) < pInfo->fileHeader.chunkTable.size);
  49.  
  50.    return pInfo->fileHeader.chunkTable.offset + cnkNum * sizeof(xFiles_chunk);
  51. }
  52.  
  53. unsigned xFiles_roundDown(xFiles_info *pInfo, unsigned pos)
  54. {
  55.    ASSERT(pInfo->fileHeader.allocationUnit == 1024);
  56.    return pos & ~(pInfo->fileHeader.allocationUnit-1);
  57. }
  58.  
  59. unsigned xFiles_roundUp(xFiles_info *pInfo, unsigned pos)
  60. {
  61.    return xFiles_roundDown(pInfo, pos + pInfo->fileHeader.allocationUnit - 1);
  62. }
  63.  
  64. #ifndef NOCHUNKCACHE
  65. static int xFiles__cacheLookup(xFiles_info *pInfo, unsigned cnkNum)
  66. {
  67.    int lo, mid, hi;
  68.  
  69.    for (lo = 0, hi = pInfo->cacheUsed-1; lo <= hi; )
  70.    {
  71.       mid = (lo + hi) / 2;
  72.       if (pInfo->chunkCache[mid].cnkNum < cnkNum)
  73.          lo = mid + 1;
  74.       else if (pInfo->chunkCache[mid].cnkNum > cnkNum)
  75.          hi = mid - 1;
  76.       else  /* cache hit */
  77.          return mid;
  78.    }
  79.  
  80.    return -1;
  81. }
  82. #endif
  83.  
  84. _kernel_oserror *xFiles_getChunkInfo(xFiles_info *pInfo, unsigned cnkNum, xFiles_chunk *pChunk)
  85. {
  86.    _kernel_oserror *err;
  87. #ifndef NOCHUNKCACHE
  88.    int cacheIndex = xFiles__cacheLookup(pInfo, cnkNum);
  89.  
  90.    if (cacheIndex != -1)
  91.    {
  92.       *pChunk = pInfo->chunkCache[cacheIndex].chunk;
  93.       xFiles_checkChunk(pInfo, pChunk);
  94.       return NULL;
  95.    }
  96. #endif
  97.  
  98.    if (err = xFiles_read(pInfo, pChunk,
  99.                          xFiles_chunkAddress(pInfo, cnkNum),
  100.                          sizeof(xFiles_chunk)), err)
  101.       return err;
  102.  
  103. #ifndef NOCHUNKCACHE
  104.    if (pInfo->cacheUsed >= xFiles_CHUNKCACHE)
  105.    {
  106.       pInfo->cacheUsed--;
  107.       cacheIndex = rand() % xFiles_CHUNKCACHE;
  108.       memmove(&pInfo->chunkCache[cacheIndex], &pInfo->chunkCache[cacheIndex+1],
  109.               sizeof(xFiles_chunkCacheItem) * (pInfo->cacheUsed - cacheIndex));
  110.    }
  111.  
  112.    /* Got a space in the cache now */
  113.  
  114.    for (cacheIndex = 0;
  115.         cacheIndex < pInfo->cacheUsed && cnkNum > pInfo->chunkCache[cacheIndex].cnkNum;
  116.         cacheIndex++)
  117.       ;
  118.  
  119.    memmove(&pInfo->chunkCache[cacheIndex+1], &pInfo->chunkCache[cacheIndex],
  120.            sizeof(xFiles_chunkCacheItem) * (pInfo->cacheUsed - cacheIndex));
  121.    pInfo->cacheUsed++;
  122.  
  123.    pInfo->chunkCache[cacheIndex].cnkNum = cnkNum;
  124.    pInfo->chunkCache[cacheIndex].chunk  = *pChunk;
  125. #endif
  126.  
  127.    return NULL;
  128. }
  129.  
  130. _kernel_oserror *xFiles_setChunkInfo(xFiles_info *pInfo, unsigned cnkNum, const xFiles_chunk *pChunk)
  131. {
  132.    _kernel_oserror *err;
  133.    int cacheIndex;
  134.  
  135.    xFiles_checkChunk(pInfo, pChunk);
  136.  
  137.    /* There has to be a special case for cnkNum == 0 which updates
  138.     * the cached information in the header before the actual index
  139.     * is written
  140.     */
  141.  
  142.    if (cnkNum == 0)
  143.    {
  144.       if (pChunk->offset < sizeof(xFiles_header))
  145.       {
  146.          TRACE("Attempt to write chunk %d with illegal offset %08x\n", cnkNum, pChunk->offset);
  147.       }
  148.  
  149.       ASSERT(pChunk->usage != FREE);
  150.       ASSERT(pChunk->offset >= sizeof(xFiles_header));
  151.  
  152.       pInfo->fileHeader.chunkTable = *pChunk;
  153.    }
  154.  
  155.    if (err = xFiles_write(pInfo, (void *) pChunk, xFiles_chunkAddress(pInfo, cnkNum), sizeof(xFiles_chunk)), err)
  156.       return err;
  157.  
  158.    /* Write through cache */
  159.  
  160. #ifndef NOCHUNKCACHE
  161.    if (cacheIndex = xFiles__cacheLookup(pInfo, cnkNum), cacheIndex != -1)
  162.    {
  163.       pInfo->chunkCache[cacheIndex].cnkNum = cnkNum;
  164.       pInfo->chunkCache[cacheIndex].chunk  = *pChunk;
  165.    }
  166. #endif
  167.  
  168.    if (cnkNum == 0)
  169.       return xFiles_updateHeader(pInfo);
  170.    else
  171.       return NULL;
  172. }
  173.  
  174. _kernel_oserror *xFiles_readChunk(xFiles_info *pInfo, void *pBuffer,
  175.                                   unsigned cnkNum, unsigned pos, unsigned size)
  176. {
  177.    _kernel_oserror *err;
  178.    xFiles_chunk chunk;
  179.  
  180.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  181.       return err;
  182.  
  183.    if (pos + size > chunk.allocSize)
  184.    {
  185.       TRACE("readChunk: chunk = [%08x, %08x], cnkNum = %08x, pos = %08x, size = %08x\n",
  186.              chunk.offset, chunk.size, cnkNum, pos, size);
  187.       return &xFiles_Outside;
  188.    }
  189.  
  190.    return xFiles_read(pInfo, pBuffer, chunk.offset + pos, size);
  191. }
  192.  
  193. _kernel_oserror *xFiles_erase(xFiles_info *pInfo, unsigned pos, unsigned size)
  194. {
  195. #ifdef ERASE
  196.    _kernel_oserror *err;
  197.    unsigned chunk;
  198.  
  199.    if (err = xFiles_claimBuffer(size), err)
  200.       return err;
  201.  
  202.    memset(xFiles_windowBuffer, 0xAA, _min(size, xFiles_windowBufferSize));
  203.  
  204.    while (size > 0)
  205.    {
  206.       chunk = _min(size, xFiles_windowBufferSize);
  207.       if (err = xFiles_write(pInfo, xFiles_windowBuffer, pos, chunk), err)
  208.          goto fail;
  209.       pos += chunk;
  210.       size -= chunk;
  211.    }
  212.    return xFiles_releaseBuffer();
  213.  
  214. fail:
  215.    (void) xFiles_releaseBuffer();
  216.    return err;
  217. #else
  218.    (void) pInfo;
  219.    (void) pos;
  220.    (void) size;
  221.    return NULL;
  222. #endif
  223. }
  224.  
  225. _kernel_oserror *xFiles_writeChunk(xFiles_info *pInfo, void *pBuffer,
  226.                                    unsigned cnkNum, unsigned pos, unsigned size)
  227. {
  228.    _kernel_oserror *err;
  229.    xFiles_chunk chunk;
  230.  
  231.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  232.       return err;
  233.  
  234.    if (pos + size > chunk.allocSize)
  235.    {
  236.       TRACE("writeChunk: chunk = [%08x, %08x], pos = %08x, size = %08x\n",
  237.              chunk.offset, chunk.size, pos, size);
  238.       return &xFiles_Outside;
  239.    }
  240.  
  241.    return xFiles_write(pInfo, pBuffer, chunk.offset + pos, size);
  242. }
  243.  
  244. _kernel_oserror *xFiles_writeAndGrow(xFiles_info *pInfo, void *pBuffer,
  245.                                      unsigned cnkNum, unsigned pos, unsigned size)
  246. {
  247.    _kernel_oserror *err;
  248.    xFiles_chunk chunk;
  249.  
  250.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  251.       return err;
  252.  
  253.    if (pos + size > chunk.size)
  254.    {
  255.       if (err = xFiles_setChunkSize(pInfo, cnkNum, pos + size), err)
  256.          return err;
  257.    }
  258.  
  259.    return xFiles_write(pInfo, pBuffer, chunk.offset + pos, size);
  260. }
  261.  
  262. _kernel_oserror *xFiles_squashFile(xFiles_info *pInfo, BOOL force)
  263. {
  264.    _kernel_oserror *err;
  265.    _kernel_swi_regs regs;
  266.    unsigned fileLength;
  267.    int now, compactAge;
  268.  
  269. #ifndef NOCOMPACT
  270.    if (err = xFiles_getLength(pInfo, &fileLength), err)
  271.       return err;
  272.  
  273.    (void) _kernel_swi(OS_ReadMonotonicTime, ®s, ®s);
  274.    now = regs.r[0];
  275.  
  276.    compactAge = now - pInfo->lastCompact;
  277.    if (force || compactAge > 3000)
  278.    {
  279.       if (pInfo->fileHeader.waste > 1024 && fileLength / pInfo->fileHeader.waste < 5)
  280.          return xFiles_Compact(pInfo);
  281.  
  282.       pInfo->lastCompact = now;
  283.    }
  284. #endif
  285.  
  286.    return NULL;
  287. }
  288.  
  289. _kernel_oserror *xFiles_updateHeader(xFiles_info *pInfo)
  290. {
  291.    _kernel_oserror *err;
  292.  
  293.    if (err = xFiles_write(pInfo, &pInfo->fileHeader, 0, sizeof(pInfo->fileHeader)), err)
  294.       return err;
  295.  
  296.    return NULL;
  297. }
  298.  
  299. _kernel_oserror *xFiles_moveToEnd(xFiles_info *pInfo, unsigned cnkNum)
  300. {
  301.    xFiles_chunk chunk;
  302.    _kernel_oserror *err;
  303.    unsigned newOffset, copyFrom, copyTo, totalSize, bitSize;
  304.  
  305.    /*TRACE("Attempting to move chunk %d to the end of the file\n", cnkNum);*/
  306.  
  307.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  308.       return err;
  309.  
  310.    if (err = xFiles_getLength(pInfo, &newOffset), err)
  311.       return err;
  312.  
  313.    if (err = xFiles_claimBuffer(chunk.allocSize), err)
  314.       return err;
  315.  
  316.    copyFrom  = chunk.offset;
  317.    copyTo    = newOffset;
  318.    totalSize = chunk.allocSize;
  319.  
  320.    if (err = xFiles_setLength(pInfo, newOffset + totalSize), err)
  321.       return err;
  322.  
  323.    while (totalSize > 0)
  324.    {
  325.       bitSize = _min(totalSize, xFiles_windowBufferSize);
  326.  
  327.       if (err = xFiles_read(pInfo, xFiles_windowBuffer, copyFrom, bitSize), err)
  328.          goto truncate;
  329.       if (err = xFiles_write(pInfo, xFiles_windowBuffer, copyTo, bitSize), err)
  330.          goto truncate;
  331.  
  332.       copyFrom  += bitSize;
  333.       copyTo    += bitSize;
  334.       totalSize -= bitSize;
  335.    }
  336.  
  337.    if (err = xFiles_releaseBuffer(), err)
  338.       return err;
  339.  
  340.    /* Copied OK, so update the information */
  341.  
  342.    chunk.offset = newOffset;
  343.    if (err = xFiles_setChunkInfo(pInfo, cnkNum, &chunk), err)
  344.       goto truncate;
  345.  
  346.    return NULL;
  347.  
  348.    /* If copying fails (probably because the disc is full or something) then
  349.     * attempt to truncate the image file back to the length it had before we
  350.     * started messing about.
  351.     */
  352.  
  353. truncate:
  354.    (void) xFiles_releaseBuffer();
  355.    (void) xFiles_setLength(pInfo, newOffset);
  356.    return err;
  357. }
  358.  
  359. /* Unhook a chunk by placing it's reference in the free list and counting
  360.  * the space it used as wasted.
  361.  */
  362. _kernel_oserror *xFiles_freeChunk(xFiles_info *pInfo, unsigned cnkNum)
  363. {
  364.    _kernel_oserror *err;
  365.    xFiles_chunk chunk;
  366.  
  367.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  368.       return err;
  369.  
  370.    if (err = xFiles_erase(pInfo, chunk.offset, chunk.size), err)
  371.       return err;
  372.  
  373.    pInfo->fileHeader.waste += chunk.size;
  374.  
  375.    chunk.offset    = pInfo->fileHeader.freeChunk;
  376.    chunk.size      =
  377.    chunk.usage     =
  378.    chunk.allocSize = FREE;  /* 'FREE' */
  379.  
  380.    pInfo->fileHeader.freeChunk = cnkNum;
  381.  
  382.    if (err = xFiles_setChunkInfo(pInfo, cnkNum, &chunk), err)
  383.       return err;
  384.  
  385.    return xFiles_updateHeader(pInfo);
  386. }
  387.  
  388. /* Main allocation routine. This sets the length of a chunk. At the moment it
  389.  * simplistically always moves the chunk to the end of the file.
  390.  */
  391.  
  392. _kernel_oserror *xFiles_setChunkSize(xFiles_info *pInfo, unsigned cnkNum, unsigned newSize)
  393. {
  394.    _kernel_oserror *err;
  395.    unsigned fileSize;
  396.    xFiles_chunk chunk;
  397.    BOOL isAtEnd;
  398.    unsigned newAllocSize;
  399.    BOOL changedHeader = FALSE;
  400.  
  401.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  402.       return err;
  403.  
  404.    if (err = xFiles_getLength(pInfo, &fileSize), err)
  405.       return err;
  406.  
  407.    if (1)
  408.    {
  409.       unsigned chunkEnd = chunk.offset + chunk.allocSize;
  410.       if (chunkEnd > fileSize)
  411.          TRACE("Hmm, chunkEnd = %08x, fileSize = %08x\n", chunkEnd, fileSize);
  412.       else if (chunkEnd < fileSize && chunkEnd > fileSize - pInfo->fileHeader.allocationUnit + 1)
  413.          TRACE("How queer, chunkEnd = %08x, fileSize = %08x\n", chunkEnd, fileSize);
  414.       if (chunk.offset != xFiles_roundDown(pInfo, chunk.offset))
  415.          TRACE("chunk.offset = %08x\n", chunk.offset);
  416.       if (chunk.allocSize != xFiles_roundDown(pInfo, chunk.allocSize))
  417.          TRACE("chunk.allocSize = %08x\n", chunk.allocSize);
  418.       if (chunk.allocSize < xFiles_roundUp(pInfo, chunk.size))
  419.          TRACE("chunk.size = %08x, chunk.allocSize = %08x\n", chunk.size, chunk.allocSize);
  420.       if (fileSize != xFiles_roundDown(pInfo, fileSize))
  421.          TRACE("fileSize = %08x\n", fileSize);   
  422.    }
  423.  
  424.    isAtEnd = (chunk.offset + chunk.allocSize) == fileSize;
  425.  
  426.    newAllocSize = xFiles_roundUp(pInfo, newSize);
  427.  
  428.    /* Growing */
  429.  
  430.    if (newAllocSize > chunk.allocSize)
  431.    {
  432.       if (!isAtEnd)
  433.       {
  434.          if (err = xFiles_moveToEnd(pInfo, cnkNum), err)
  435.             return err;
  436.  
  437.          if (err = xFiles_erase(pInfo, chunk.offset, chunk.allocSize), err)
  438.             return err;
  439.  
  440.          chunk.offset = fileSize;
  441.          fileSize += chunk.allocSize;
  442.          isAtEnd = TRUE;
  443.  
  444.          pInfo->fileHeader.waste += chunk.allocSize;
  445.          changedHeader = TRUE;
  446.       }
  447.    }
  448.  
  449.    /* Growing or shrinking */
  450.  
  451.    if (isAtEnd)
  452.    {
  453.       if (err = xFiles_setLength(pInfo, fileSize - chunk.allocSize + newAllocSize), err)
  454.          return err;
  455.  
  456.       chunk.allocSize = newAllocSize;
  457.    }
  458.    else
  459.    {
  460.       int oldWaste = chunk.allocSize - xFiles_roundUp(pInfo, chunk.size);
  461.       int newWaste = newAllocSize    - xFiles_roundUp(pInfo, newSize);
  462.  
  463.       pInfo->fileHeader.waste += (newWaste - oldWaste);
  464.       if (newWaste != oldWaste)
  465.          changedHeader = TRUE;
  466.    }
  467.  
  468.    chunk.size = newSize;
  469.  
  470.    if (err = xFiles_setChunkInfo(pInfo, cnkNum, &chunk), err)
  471.       return err;
  472.  
  473.    if (changedHeader && (err = xFiles_updateHeader(pInfo), err))
  474.       return err;
  475.  
  476.    return NULL;
  477. }
  478.  
  479. /* Move a block in the file without trashing it */
  480.  
  481. _kernel_oserror *xFiles_moveBlock(xFiles_info *pInfo, unsigned to, unsigned from, int size)
  482. {
  483.    _kernel_oserror *err;
  484.  
  485.    if (size < 0)
  486.       return &xFiles_OutOfRange;
  487.  
  488.    if (from == to || size == 0)
  489.       return NULL;
  490.  
  491.    if (err = xFiles_claimBuffer(size), err)
  492.       return err;
  493.  
  494.    if (to < from)
  495.    {
  496.       int fragSize;
  497.  
  498.       while (size > 0)
  499.       {
  500.          fragSize = _min(size, xFiles_windowBufferSize);
  501.  
  502.          if (err = xFiles_read(pInfo, xFiles_windowBuffer, from, fragSize), err)
  503.             goto failed;
  504.          if (err = xFiles_write(pInfo, xFiles_windowBuffer, to, fragSize), err)
  505.             goto failed;
  506.  
  507.          from += fragSize;
  508.          to   += fragSize;
  509.          size -= fragSize;
  510.       }
  511.    }
  512.    else if (to > from)
  513.    {
  514.       int fragSize;
  515.       from += size;
  516.       to   += size;
  517.  
  518.       while (size > 0)
  519.       {
  520.          fragSize = _min(size, xFiles_windowBufferSize);
  521.  
  522.          from -= fragSize;
  523.          to   -= fragSize;
  524.  
  525.          if (err = xFiles_read(pInfo, xFiles_windowBuffer, from, fragSize), err)
  526.             goto failed;
  527.          if (err = xFiles_write(pInfo, xFiles_windowBuffer, to, fragSize), err)
  528.             goto failed;
  529.  
  530.          size -= fragSize;
  531.       }
  532.    }
  533.  
  534.    return xFiles_releaseBuffer();
  535.  
  536. failed:
  537.    (void) xFiles_releaseBuffer();
  538.    return err;
  539. }
  540.  
  541. /* Make a space in the middle of a chunk. If 'by' is positive everything from pos
  542.  * upwards will be moved up by pos bytes. If 'by' is negative everything from pos
  543.  * upwards will be moved down by pos bytes. In other words if 'by' is negative it
  544.  * deletes backwards rather than deleting forwards.
  545.  */
  546.  
  547. _kernel_oserror *xFiles_midExtend(xFiles_info *pInfo, unsigned cnkNum, int pos, int by)
  548. {
  549.    _kernel_oserror *err;
  550.    xFiles_chunk chunk;
  551.    int amountToMove;
  552.  
  553.    if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  554.       return err;
  555.  
  556.    if (pos > chunk.size || pos < 0 || pos + by < 0)
  557.       return &xFiles_OutOfRange;
  558.  
  559.    amountToMove = chunk.size - pos;
  560.  
  561.    if (by > 0)
  562.    {
  563.       /* Extend it first */
  564.  
  565.       if (err = xFiles_setChunkSize(pInfo, cnkNum, chunk.size + by), err)
  566.          return err;
  567.  
  568.       /* Chunk may have moved so we need to find out where it is again */
  569.  
  570.       if (err = xFiles_getChunkInfo(pInfo, cnkNum, &chunk), err)
  571.          return err;
  572.  
  573.       if (err = xFiles_moveBlock(pInfo, chunk.offset + pos + by, chunk.offset + pos, amountToMove), err)
  574.          return err;
  575.    }
  576.    else if (by < 0)
  577.    {
  578.       if (err = xFiles_moveBlock(pInfo, chunk.offset + pos + by, chunk.offset + pos, amountToMove), err)
  579.          return err;
  580.  
  581.       if (err = xFiles_setChunkSize(pInfo, cnkNum, chunk.size + by), err)
  582.          return err;
  583.    }
  584.  
  585.    return NULL;
  586. }
  587.  
  588. /* Make the free chunk list non-empty
  589.  */
  590.  
  591. _kernel_oserror *xFiles_makeFreeChunks(xFiles_info *pInfo)
  592. {
  593.    _kernel_oserror *err;
  594.    xFiles_chunk chunkTable;
  595.    xFiles_chunk newChunk;
  596.    int c;
  597.    unsigned nChunks;
  598.  
  599.    if (pInfo->fileHeader.freeChunk != 0)
  600.       return NULL;
  601.  
  602.    /* The free list is empty, so extend the chunk table and add a few
  603.     * new entries to the free list.
  604.     */
  605.  
  606.    if (err = xFiles_getChunkInfo(pInfo, 0, &chunkTable), err)
  607.       return err;
  608.  
  609.    nChunks = chunkTable.size / sizeof(xFiles_chunk);  /* how many just now? */
  610.  
  611.    /* Grow the table */
  612.  
  613.    if (err = xFiles_setChunkSize(pInfo, 0, chunkTable.size + xFiles_NEWCHUNKS * sizeof(xFiles_chunk)), err)
  614.       return err;
  615.  
  616.    newChunk.offset    = 0;
  617.    newChunk.size      =
  618.    newChunk.usage     =
  619.    newChunk.allocSize = FREE;  /* 'FREE' */
  620.  
  621.    for (c = 0; c < xFiles_NEWCHUNKS; c++)
  622.    {
  623.       if (err = xFiles_setChunkInfo(pInfo, nChunks + c, &newChunk), err)
  624.          return err;
  625.  
  626.       newChunk.offset = nChunks + c;  /* Ready for next one */
  627.   }
  628.  
  629.   pInfo->fileHeader.freeChunk = newChunk.offset;
  630.  
  631.   return xFiles_updateHeader(pInfo);
  632. }
  633.  
  634. /* Create a new chunk with zero length.
  635.  */
  636.  
  637. _kernel_oserror *xFiles_newChunk(xFiles_info *pInfo, unsigned *pCnkNum)
  638. {
  639.    _kernel_oserror *err;
  640.    xFiles_chunk freeChunk;
  641.    unsigned newChunk;
  642.  
  643.    if (err = xFiles_makeFreeChunks(pInfo), err)
  644.       return err;
  645.  
  646.    newChunk = pInfo->fileHeader.freeChunk;
  647.  
  648.    if (err = xFiles_getChunkInfo(pInfo, newChunk, &freeChunk), err)
  649.       return err;
  650.  
  651.    pInfo->fileHeader.freeChunk = freeChunk.offset;    /* chain to next free chunk */
  652.  
  653.    if (err = xFiles_updateHeader(pInfo), err)
  654.       return err;
  655.  
  656.    if (err = xFiles_getLength(pInfo, &freeChunk.offset), err)
  657.       return err;
  658.  
  659.    freeChunk.size  = 0;
  660.    freeChunk.usage = 1;      /* There must be one user, surely? */
  661.    freeChunk.allocSize = xFiles_roundUp(pInfo, 1);  /* make it one block long */
  662.  
  663.    if (err = xFiles_setLength(pInfo, freeChunk.offset + freeChunk.allocSize), err)
  664.       return err;
  665.  
  666.    if (err = xFiles_setChunkInfo(pInfo, newChunk, &freeChunk), err)
  667.       return err;
  668.  
  669.    if (pCnkNum) *pCnkNum = newChunk;
  670.  
  671.    return NULL;
  672. }
  673.  
  674. /* Build a new, empty filesystem in the specified file.
  675.  */
  676.  
  677. _kernel_oserror *xFiles_buildNewFilesystem(xFiles_info *pInfo)
  678. {
  679.    _kernel_oserror *err;
  680.    unsigned tablePos;
  681.  
  682.    if (err = xFiles_setLength(pInfo, 0), err)  /* truncate */
  683.       return err;
  684.  
  685.    pInfo->fileHeader.allocationUnit    = xFiles_ALLOCATIONUNIT;
  686.  
  687.    tablePos = xFiles_roundUp(pInfo, sizeof(xFiles_header));
  688.  
  689.    pInfo->fileHeader.sig                  = xFiles_SIG;
  690.    pInfo->fileHeader.hdrSize              = sizeof(xFiles_header);  /* surprise! */
  691.    pInfo->fileHeader.structureVersion     = xFiles_STRUCTUREVERSION;
  692.    pInfo->fileHeader.directoryVersion     = xFiles_DIRECTORYVERSION;
  693.    pInfo->fileHeader.chunkTable.offset    = tablePos;
  694.    pInfo->fileHeader.chunkTable.size      = sizeof(xFiles_chunk);
  695.    pInfo->fileHeader.chunkTable.usage     = 1;
  696.    pInfo->fileHeader.chunkTable.allocSize = xFiles_roundUp(pInfo, sizeof(xFiles_chunk));
  697.    pInfo->fileHeader.rootChunk            = 0;
  698.    pInfo->fileHeader.freeChunk            = 0;
  699.    pInfo->fileHeader.waste                = 0;
  700.  
  701.    if (err = xFiles_write(pInfo, &pInfo->fileHeader, 0, sizeof(pInfo->fileHeader)), err)
  702.       return err;
  703.  
  704.    /* Write a second copy of the chunkTable entry in the header. This is actually the
  705.     * 1 entry long chunktable in this minimal filesystem.
  706.     */
  707.  
  708.    if (err = xFiles_write(pInfo, &pInfo->fileHeader.chunkTable, tablePos, sizeof(xFiles_chunk)), err)
  709.       return err;
  710.  
  711.    if (err = xFiles_setLength(pInfo, tablePos + pInfo->fileHeader.chunkTable.allocSize), err)
  712.       return err;
  713.  
  714.    if (err = xFiles_cDir(pInfo, 0, &pInfo->fileHeader.rootChunk), err)
  715.       return err;
  716.  
  717.    if (err = xFiles_updateHeader(pInfo), err)
  718.       return err;
  719.  
  720.    return NULL;
  721. }
  722.  
  723. /* The specified image file is open, so update our flags.
  724.  */
  725.  
  726. _kernel_oserror *xFiles_OpenImage(xFiles_info *pInfo)
  727. {
  728.    _kernel_swi_regs regs;
  729.    _kernel_oserror *err;
  730.    unsigned imageSize;
  731.  
  732.    pInfo->compacting = FALSE;
  733.    pInfo->cacheUsed = 0;
  734.  
  735.    (void) _kernel_swi(OS_ReadMonotonicTime, ®s, ®s);
  736.    pInfo->lastCompact = regs.r[0];
  737.  
  738.    pInfo->readOnly = FALSE;          /* doesn't seem to work */
  739.    pInfo->fileSize = (unsigned) -1;  /* impossible value     */
  740.    pInfo->flags    = 0;              /* nothing for now      */
  741.  
  742.    if (err = xFiles_getLength(pInfo, &imageSize), err)
  743.       return err;
  744.  
  745.    /* Handle the special case where the image is zero length by building the default
  746.     * header, chunk table and root directory
  747.     */
  748.  
  749.    if (imageSize == 0)
  750.    {
  751.       if (pInfo->readOnly)
  752.          return &xFiles_CantFake;
  753.  
  754.       if (err = xFiles_buildNewFilesystem(pInfo), err)
  755.          return err;
  756.    }
  757.    else
  758.    {
  759.       if (err = xFiles_read(pInfo, &pInfo->fileHeader, 0, sizeof(pInfo->fileHeader)), err)
  760.          return err;
  761.  
  762.       if (pInfo->fileHeader.sig != xFiles_SIG)
  763.          return &xFiles_ImageCorrupt;
  764.  
  765.       /* If the image is a funny size it probably wasn't closed properly so we
  766.        * need to compact it otherwise future allocations will be non-aligned
  767.        */
  768.  
  769.       if (imageSize != xFiles_roundDown(pInfo, imageSize))
  770.       {
  771.          if (err = xFiles_Compact(pInfo), err)
  772.             return err;
  773.       }
  774.    }
  775.  
  776.    /* No error, so link it in */
  777.  
  778.    xFiles_AddAtHead(&xFiles_openFiles, &pInfo->li);
  779.  
  780.    return NULL;
  781. }
  782.  
  783. _kernel_oserror *xFiles_CloseImage(xFiles_info *pInfo)
  784. {
  785.    _kernel_oserror *err;
  786.  
  787.    if (err = xFiles_FlushFileInfo(pInfo), err)
  788.       return err;
  789.  
  790.    if (err = xFiles_squashFile(pInfo, TRUE), err)
  791.       return err;
  792.  
  793.    xFiles_Remove(&xFiles_openFiles, &pInfo->li);
  794.  
  795.    free(pInfo);
  796.    return NULL;
  797. }
  798.  
  799. /*********************************************************************************/
  800. /*                                                                               */
  801. /*   Directory functions                                                         */
  802. /*                                                                               */
  803. /*   - Create an empty directory                                                 */
  804. /*   - Create an entry                                                           */
  805. /*   - Lookup an entry                                                           */
  806. /*   - Update file details                                                       */
  807. /*   - Change name (not actually needed: can use create entry / remove entry)    */
  808. /*   - Remove an entry                                                           */
  809. /*   - Fill a buffer with a selection of entries                                 */
  810. /*                                                                               */
  811. /*********************************************************************************/
  812.  
  813. /* Read a directory's header
  814.  */
  815.  
  816. _kernel_oserror *xFiles_getDirHeader(xFiles_info *pInfo, unsigned dirObject, xFiles_dirHeader *pHdr)
  817. {
  818.    _kernel_oserror *err;
  819.  
  820.    if (err = xFiles_readChunk(pInfo, (void *) pHdr, dirObject, 0, sizeof(xFiles_dirHeader)), err)
  821.       return err;
  822.  
  823.    if (pHdr->sig != xFiles_DIRSIG)
  824.    {
  825.       xFiles_chunk dirChunk;
  826.       (void) xFiles_getChunkInfo(pInfo, dirObject, &dirChunk);
  827.       TRACE("Directory object %d broken (%08x, %08x)\n", dirObject, dirChunk.offset, dirChunk.size);
  828.       return &xFiles_BrokenDir;
  829.    }
  830.  
  831.    return NULL;
  832. }
  833.  
  834. /* Write a directory's header
  835.  */
  836.  
  837. _kernel_oserror *xFiles_setDirHeader(xFiles_info *pInfo, unsigned dirObject, const xFiles_dirHeader *pHdr)
  838. {
  839.    _kernel_oserror *err;
  840.  
  841.    if (pHdr->sig != xFiles_DIRSIG)
  842.       return &xFiles_BadDirMark;
  843.  
  844.    if (err = xFiles_writeChunk(pInfo, (void *) pHdr, dirObject, 0, sizeof(xFiles_dirHeader)), err)
  845.       return err;
  846.  
  847.    return NULL;
  848. }
  849.  
  850. /* Make space in a directory's hash table. This might involve relocating
  851.  * the directory's contents -- caller beware
  852.  */
  853.  
  854. _kernel_oserror *xFiles_makeSpaceInHashTable(xFiles_info *pInfo, unsigned dirObject)
  855. {
  856.    xFiles_dirHeader dirHdr;
  857.    xFiles_chunk     dirChunk;
  858.    _kernel_oserror *err;
  859.  
  860.    if (err = xFiles_getChunkInfo(pInfo, dirObject, &dirChunk), err)
  861.       return err;
  862.  
  863.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  864.       return err;
  865.  
  866.    if (dirHdr.used >= dirHdr.size)
  867.    {
  868.       int shiftBy  = sizeof(xFiles_dirHash) * xFiles_GROWDIRBY;
  869.       int hashPos  = sizeof(xFiles_dirHeader);
  870.       int shiftPos = hashPos + sizeof(xFiles_dirHash) * dirHdr.size;
  871.  
  872.       if (err = xFiles_midExtendDirectory(pInfo, dirObject, shiftPos, shiftBy), err)
  873.          return err;
  874.  
  875.       dirHdr.size += xFiles_GROWDIRBY;
  876.  
  877.       if (err = xFiles_setDirHeader(pInfo, dirObject, &dirHdr), err)
  878.          return err;
  879.    }
  880.  
  881.    return NULL;
  882. }
  883.  
  884. _kernel_oserror *xFiles_midExtendDirectory(xFiles_info *pInfo, unsigned dirObject, int pos, int by)
  885. {
  886.    _kernel_oserror *err;
  887.  
  888.    if (err = xFiles_midExtend(pInfo, dirObject, pos, by), err)
  889.       return err;
  890.  
  891.    if (err = xFiles_relocateHashes(pInfo, dirObject, pos, by), err)
  892.       return err;
  893.  
  894.    return NULL;
  895. }
  896.  
  897. /* Modify the hash table in a directory after changing the size of the directory. All hash
  898.  * offsets which are >= pos will be relocated by 'by'. When this is called the directory
  899.  * header's used field will be valid, but size might not be. Anyway size doesn't matter.
  900.  */
  901.  
  902. _kernel_oserror *xFiles_relocateHashes(xFiles_info *pInfo, unsigned dirObject, int pos, int by)
  903. {
  904.    xFiles_dirHeader dirHdr;
  905.    xFiles_chunk     dirChunk;
  906.    _kernel_oserror *err;
  907.    int hashSize, hashPos, bitSize;
  908.  
  909.    if (err = xFiles_getChunkInfo(pInfo, dirObject, &dirChunk), err)
  910.       return err;
  911.  
  912.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  913.       return err;
  914.  
  915.    hashPos  = sizeof(xFiles_dirHeader);
  916.    hashSize = dirHdr.used * sizeof(xFiles_dirHash);
  917.  
  918.    if (err = xFiles_claimBuffer(hashSize), err)
  919.       return err;
  920.  
  921.    /* Now set about reading the whole hash table and updating it */
  922.  
  923.    while (hashSize > 0)
  924.    {
  925.       int howMany;
  926.       xFiles_dirHash *pHash;
  927.       BOOL modified;
  928.  
  929.       bitSize = _min(hashSize, xFiles_windowBufferSize);
  930.  
  931.       howMany = bitSize / sizeof(xFiles_dirHash);
  932.       bitSize = howMany * sizeof(xFiles_dirHash);
  933.  
  934.       if (err = xFiles_readChunk(pInfo, xFiles_windowBuffer, dirObject, hashPos, bitSize), err)
  935.          goto failed;
  936.  
  937.       /* Update them */
  938.  
  939.       for (modified = FALSE, pHash = (xFiles_dirHash *) xFiles_windowBuffer; howMany > 0; pHash++, howMany--)
  940.       {
  941.          if (pHash->entryPos >= pos)
  942.          {
  943.             pHash->entryPos += by;
  944.             modified = TRUE;
  945.          }
  946.       }
  947.  
  948.       if (modified)
  949.       {
  950.          if (err = xFiles_writeChunk(pInfo, xFiles_windowBuffer, dirObject, hashPos, bitSize), err)
  951.             goto failed;
  952.       }
  953.  
  954.       hashSize -= bitSize;
  955.       hashPos  += bitSize;
  956.    }
  957.  
  958.    return xFiles_releaseBuffer();
  959.  
  960. failed:
  961.    (void) xFiles_releaseBuffer();
  962.    return err;
  963. }
  964.  
  965. /* Create an empty directory returning it's chunk number */
  966.  
  967. _kernel_oserror *xFiles_cDir(xFiles_info *pInfo, unsigned parent, unsigned *pCnkNum)
  968. {
  969.    xFiles_dirHeader dir;
  970.    _kernel_oserror *err;
  971.    unsigned cnkNum;
  972.  
  973.    dir.sig     = xFiles_DIRSIG;
  974.    dir.parent  = parent;
  975.    dir.size    =
  976.    dir.used    = 0;
  977.  
  978.    if (err = xFiles_newChunk(pInfo, &cnkNum), err)
  979.       return err;
  980.  
  981.    if (err = xFiles_writeAndGrow(pInfo, &dir, cnkNum, 0, sizeof(dir)), err)
  982.       return err;
  983.  
  984.    if (pCnkNum) *pCnkNum = cnkNum;
  985.    return NULL;
  986. }
  987.  
  988. /* Create an entry in a directory. This call assumes that the entry doesn't already
  989.  * exist and doesn't do any checks to that effect.
  990.  */
  991.  
  992. _kernel_oserror *xFiles_createDirEntry(xFiles_info *pInfo, unsigned dirObject,
  993.                                        xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt,
  994.                                        const char *pName)
  995. {
  996.    _kernel_oserror *err;
  997.    xFiles_dirHeader dirHdr;
  998.    xFiles_dirHash   dirHash;
  999.    xFiles_chunk     dirChunk;
  1000.    int entrySize, nameLen;
  1001.  
  1002.    if (err = xFiles_makeSpaceInHashTable(pInfo, dirObject), err)
  1003.       return err;
  1004.  
  1005.    /* At this stage it's guaranteed that there's at least one free space
  1006.     * in the hash table.
  1007.     */
  1008.  
  1009.    if (err = xFiles_getChunkInfo(pInfo, dirObject, &dirChunk), err)
  1010.       return err;
  1011.  
  1012.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  1013.       return err;
  1014.  
  1015.    nameLen = strlen(pName);
  1016.    pDirEnt->nameLen = nameLen;
  1017.  
  1018.    entrySize = sizeof(xFiles_dirEntry) + (nameLen + 4) & ~3;
  1019.  
  1020.    if (err = xFiles_setChunkSize(pInfo, dirObject, dirChunk.size + entrySize), err)
  1021.       return err;
  1022.  
  1023.    /* Write the entry first, then the name, then the hash, then the header */
  1024.  
  1025.    if (err = xFiles_writeChunk(pInfo, (void *) pDirEnt, dirObject,
  1026.                                dirChunk.size, sizeof(xFiles_dirEntry)), err)
  1027.       return err;
  1028.  
  1029.    if (err = xFiles_writeChunk(pInfo, (void *) pName, dirObject,
  1030.                                dirChunk.size + sizeof(xFiles_dirEntry), nameLen + 1), err)
  1031.       return err;
  1032.  
  1033.    memset(dirHash.nameStart, 0, 4);
  1034.    memcpy(dirHash.nameStart, pName, _min(nameLen, 4));
  1035.  
  1036.    dirHash.entryPos = dirChunk.size;
  1037.    dirHash.node     = pDirHash->node;
  1038.  
  1039.    if (err = xFiles_writeChunk(pInfo, &dirHash, dirObject,
  1040.                          sizeof(xFiles_dirHeader) + sizeof(xFiles_dirHash) * dirHdr.used,
  1041.                          sizeof(xFiles_dirHash)), err)
  1042.       return err;
  1043.  
  1044.    dirHdr.used++;
  1045.  
  1046.    if (err = xFiles_setDirHeader(pInfo, dirObject, &dirHdr), err)
  1047.       return err;
  1048.  
  1049.    return NULL;
  1050. }
  1051.  
  1052. static int memcicmp(const void *m1, const void *m2, size_t size)
  1053. {
  1054.    const char *c1 = m1;
  1055.    const char *c2 = m2;
  1056.  
  1057.    if (memcmp(m1, m2, size) == 0)
  1058.       return 0;
  1059.  
  1060.    while (size > 0 && tolower(*c1) == tolower(*c2))
  1061.       c1++, c2++, size--;
  1062.  
  1063.    return (size == 0) ? 0 : tolower(*c1) - tolower(*c2);
  1064. }
  1065.  
  1066. /* Primitive routine to scan a directory for the specified name. The name ends
  1067.  * at the first '.' or '\0'
  1068.  */
  1069.  
  1070. _kernel_oserror *xFiles_dirLookup(xFiles_info *pInfo, unsigned dirObject, const char *pName,
  1071.                                   unsigned *pHashOffset, unsigned *pEntryOffset,
  1072.                                   xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt)
  1073. {
  1074.    _kernel_oserror *err;
  1075.    xFiles_dirHeader dirHdr;
  1076.    xFiles_dirEntry  dirEnt;
  1077.    int              hashSize;
  1078.    int              hashPos;
  1079.    int              bitSize;
  1080.    int              nameLen;
  1081.    char            *dotPos;
  1082.    char             nameBuf[xFiles_MAXNAME+1];
  1083.    unsigned         hashOfs;
  1084.  
  1085.    if (dotPos = strchr(pName, '.'), dotPos)
  1086.       nameLen = dotPos-pName;
  1087.    else
  1088.       nameLen = strlen(pName);
  1089.  
  1090.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  1091.       return err;
  1092.  
  1093.    hashPos  = sizeof(xFiles_dirHeader);
  1094.    hashOfs  = hashPos;
  1095.    hashSize = dirHdr.used * sizeof(xFiles_dirHash);
  1096.  
  1097.    if (err = xFiles_claimBuffer(hashSize), err)
  1098.       return err;
  1099.  
  1100.    while (hashSize > 0)
  1101.    {
  1102.       int howMany;
  1103.       xFiles_dirHash *pHash;
  1104.  
  1105.       bitSize = _min(hashSize, xFiles_windowBufferSize);
  1106.  
  1107.       howMany = bitSize / sizeof(xFiles_dirHash);
  1108.       bitSize = howMany * sizeof(xFiles_dirHash);
  1109.  
  1110.       if (err = xFiles_readChunk(pInfo, xFiles_windowBuffer, dirObject, hashPos, bitSize), err)
  1111.          goto failed;
  1112.  
  1113.       for (pHash = (xFiles_dirHash *) xFiles_windowBuffer;
  1114.            howMany > 0;
  1115.            pHash++, hashOfs += sizeof(xFiles_dirHash), howMany--)
  1116.       {
  1117.  
  1118.          if (memcicmp(pName, pHash->nameStart, _min(nameLen, 4)) != 0)
  1119.             continue;
  1120.  
  1121.          if (err = xFiles_readChunk(pInfo, &dirEnt, dirObject, pHash->entryPos, sizeof(dirEnt)), err)
  1122.             goto failed;
  1123.  
  1124.          if (dirEnt.nameLen == 0 || dirEnt.nameLen > 256)
  1125.          {
  1126.             xFiles_chunk cnk;
  1127.  
  1128.             if (nameLen != 12)
  1129.                continue;
  1130.  
  1131.             memcpy(nameBuf, pHash->nameStart, 4);
  1132.             sprintf(nameBuf+4, "%08d", pHash->node);
  1133.  
  1134.             /* Fix up the rest of the directory entry */
  1135.  
  1136.             if (err = xFiles_getChunkInfo(pInfo, pHash->node, &cnk), err)
  1137.                return err;
  1138.  
  1139.             dirEnt.load =
  1140.             dirEnt.exec = 0xFFFFFFFF;
  1141.             dirEnt.size = cnk.size;
  1142.             dirEnt.attr = xFiles_meRead | xFiles_meWrite;
  1143.          }
  1144.          else
  1145.          {
  1146.             if (nameLen != dirEnt.nameLen)
  1147.                continue;
  1148.    
  1149.             if (err = xFiles_readChunk(pInfo, nameBuf, dirObject,
  1150.                                        pHash->entryPos + sizeof(dirEnt), dirEnt.nameLen+1), err)
  1151.                goto failed;
  1152.          }
  1153.  
  1154.          if (memcicmp(pName, nameBuf, nameLen) != 0)
  1155.             continue;
  1156.  
  1157.          /* Found it! */
  1158.  
  1159.          if (err = xFiles_releaseBuffer(), err)
  1160.             return err;
  1161.  
  1162.          *pHashOffset  = hashOfs;
  1163.          *pEntryOffset = pHash->entryPos;
  1164.          if (pDirEnt)  *pDirEnt  = dirEnt;
  1165.          if (pDirHash) *pDirHash = *pHash;
  1166.  
  1167.          return NULL;
  1168.       }
  1169.  
  1170.       hashSize -= bitSize;
  1171.       hashPos  += bitSize;
  1172.    }
  1173.  
  1174.    if (err = xFiles_releaseBuffer(), err)
  1175.       return err;
  1176.  
  1177.    *pHashOffset  = 0;
  1178.    *pEntryOffset = 0;
  1179.  
  1180.    return NULL;
  1181.  
  1182. failed:
  1183.    (void) xFiles_releaseBuffer();
  1184.    return err;
  1185. }
  1186.  
  1187. /* Scan a directory for a particular node.
  1188.  */
  1189.  
  1190. _kernel_oserror *xFiles_dirLookupByNode(xFiles_info *pInfo, unsigned dirObject, unsigned node,
  1191.                                         unsigned *pHashOffset, unsigned *pEntryOffset,
  1192.                                         xFiles_dirHash *pDirHash)
  1193. {
  1194.    _kernel_oserror *err;
  1195.    xFiles_dirHeader dirHdr;
  1196.    int              hashSize;
  1197.    int              hashPos;
  1198.    int              bitSize;
  1199.    unsigned         hashOfs;
  1200.  
  1201.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  1202.       return err;
  1203.  
  1204.    hashPos  = sizeof(xFiles_dirHeader);
  1205.    hashOfs  = hashPos;
  1206.    hashSize = dirHdr.used * sizeof(xFiles_dirHash);
  1207.  
  1208.    if (err = xFiles_claimBuffer(hashSize), err)
  1209.       return err;
  1210.  
  1211.    while (hashSize > 0)
  1212.    {
  1213.       int howMany;
  1214.       xFiles_dirHash *pHash;
  1215.  
  1216.       bitSize = _min(hashSize, xFiles_windowBufferSize);
  1217.  
  1218.       howMany = bitSize / sizeof(xFiles_dirHash);
  1219.       bitSize = howMany * sizeof(xFiles_dirHash);
  1220.  
  1221.       if (err = xFiles_readChunk(pInfo, xFiles_windowBuffer, dirObject, hashPos, bitSize), err)
  1222.          goto failed;
  1223.  
  1224.       for (pHash = (xFiles_dirHash *) xFiles_windowBuffer;
  1225.            howMany > 0;
  1226.            pHash++, hashOfs += sizeof(xFiles_dirHash), howMany--)
  1227.       {
  1228.          if (pHash->node == node)
  1229.          {
  1230.             if (err = xFiles_releaseBuffer(), err)
  1231.                return err;
  1232.  
  1233.             *pHashOffset  = hashOfs;
  1234.             *pEntryOffset = pHash->entryPos;
  1235.             if (pDirHash) *pDirHash = *pHash;
  1236.  
  1237.             return NULL;
  1238.          }
  1239.       }
  1240.  
  1241.       hashSize -= bitSize;
  1242.       hashPos  += bitSize;
  1243.    }
  1244.  
  1245.    if (err = xFiles_releaseBuffer(), err)
  1246.       return err;
  1247.  
  1248.    *pHashOffset  = 0;
  1249.    *pEntryOffset = 0;
  1250.  
  1251.    return NULL;
  1252.  
  1253. failed:
  1254.    (void) xFiles_releaseBuffer();
  1255.    return err;
  1256. }
  1257.  
  1258. /* Remove an entry from a directory given the offset of the entry's hash. At the moment
  1259.  * this is pretty simple minded: it just does to midExtend operations.
  1260.  */
  1261.  
  1262. _kernel_oserror *xFiles_deleteDirEntry(xFiles_info *pInfo, unsigned dirObject, unsigned hashOffset)
  1263. {
  1264.    _kernel_oserror *err;
  1265.    xFiles_dirHeader dirHdr;
  1266.    xFiles_dirHash   dirHash;
  1267.    xFiles_dirEntry  dirEnt;
  1268.    int entryPos;
  1269.    int entrySize;
  1270.  
  1271.    if (err = xFiles_getDirHeader(pInfo, dirObject, &dirHdr), err)
  1272.       return err;
  1273.  
  1274.    if (err = xFiles_readChunk(pInfo, &dirHash, dirObject, hashOffset, sizeof(dirHash)), err)
  1275.       return err;
  1276.  
  1277.    if (err = xFiles_readChunk(pInfo, &dirEnt, dirObject, dirHash.entryPos, sizeof(dirEnt)), err)
  1278.       return err;
  1279.  
  1280.    if (err = xFiles_midExtendDirectory(pInfo, dirObject, hashOffset + sizeof(dirHash), -sizeof(dirHash)), err)
  1281.       return err;
  1282.  
  1283.    entryPos  = dirHash.entryPos - sizeof(dirHash);  /* just been moved down */
  1284.    entrySize = sizeof(dirEnt) + (dirEnt.nameLen + 4) & ~3;
  1285.  
  1286.    if (err = xFiles_midExtendDirectory(pInfo, dirObject, entryPos + entrySize, -entrySize), err)
  1287.       return err;
  1288.  
  1289.    dirHdr.used--;
  1290.    dirHdr.size--;
  1291.  
  1292.    return xFiles_setDirHeader(pInfo, dirObject, &dirHdr);
  1293. }
  1294.  
  1295. /* Parse a pathname returning the parent directory (or 0 if the path is null which
  1296.  * means that this is the root), hashOffset, entryOffset and optionally directory
  1297.  * entry for the item referred to. If an element in the path other than the last is
  1298.  * actually a file then the result is the same as if the file was not found.
  1299.  */
  1300.  
  1301. _kernel_oserror *xFiles_parsePath(xFiles_info *pInfo, const char *pName, unsigned *pParent,
  1302.                                   unsigned *pHashOffset, unsigned *pEntryOffset,
  1303.                                   xFiles_dirHash *pDirHash, xFiles_dirEntry *pDirEnt,
  1304.                                   BOOL excludeLeaf)
  1305. {
  1306.    _kernel_oserror *err;
  1307.    const char *pPath, *nextDot;
  1308.    unsigned parent;
  1309.    unsigned hashOffset = 1, entryOffset = 1;
  1310.    unsigned curDir;
  1311.    xFiles_dirEntry dirEnt;
  1312.    xFiles_dirHash  dirHash;
  1313.    xFiles_chunk    rootChunk;
  1314.  
  1315.    /*TRACE("xFiles_parsePath(%p, \"%s\", %p, %p, %p, %p, excludeLeaf = %s)\n",
  1316.                   pInfo, pName, pParent, pHashOffset, pEntryOffset, pDirEnt, excludeLeaf ? "TRUE" : "FALSE");*/
  1317.  
  1318.    curDir = pInfo->fileHeader.rootChunk;
  1319.  
  1320.    if (err = xFiles_getChunkInfo(pInfo, curDir, &rootChunk), err) return err;
  1321.  
  1322.    /* Fake up a dirHash, dirEnt for the root directory */
  1323.  
  1324.    dirHash.node = curDir;
  1325.  
  1326.    dirEnt.load  =
  1327.    dirEnt.exec  = 0;
  1328.    dirEnt.attr  = xFiles_meRead | xFiles_meWrite | xFiles_isDir;
  1329.    dirEnt.size  = rootChunk.size;
  1330.  
  1331.    pPath = pName;
  1332.  
  1333.    for (;;)
  1334.    {
  1335.       parent = curDir;
  1336.  
  1337.       if (*pPath == '\0')
  1338.          goto found;
  1339.  
  1340.       nextDot = strchr(pPath, '.');
  1341.  
  1342.       if (!nextDot && excludeLeaf)
  1343.          goto found;
  1344.  
  1345.       if (err = xFiles_dirLookup(pInfo, curDir, pPath, &hashOffset, &entryOffset, &dirHash, &dirEnt), err)
  1346.          return err;
  1347.  
  1348.       if (hashOffset == 0)
  1349.          break;
  1350.  
  1351.       if (!nextDot)
  1352.          goto found;
  1353.  
  1354.       if ((dirEnt.attr & xFiles_isDir) == 0)
  1355.          break;         /* can't go on if this isn't a directory */
  1356.  
  1357.       pPath = nextDot + 1;
  1358.       curDir = dirHash.node;
  1359.    }
  1360.  
  1361.    /* Only gets here if the object couldn't be found */
  1362.  
  1363.    if (pParent)      *pParent      = 0;
  1364.    if (pHashOffset)  *pHashOffset  = 0;
  1365.    if (pEntryOffset) *pEntryOffset = 0;
  1366.  
  1367.    return NULL;
  1368.  
  1369.    /* If excludeLeaf was true the the hashOffset, entryOffset and dirEnt items
  1370.     * are not much use to the caller.
  1371.     */
  1372.  
  1373. found:
  1374.    if (pParent)      *pParent      = parent;
  1375.    if (pHashOffset)  *pHashOffset  = excludeLeaf ? 1 : hashOffset;
  1376.    if (pEntryOffset) *pEntryOffset = excludeLeaf ? 1 : entryOffset;
  1377.    if (pDirHash)     *pDirHash     = dirHash;
  1378.    if (pDirEnt)      *pDirEnt      = dirEnt;
  1379.  
  1380.    return NULL;
  1381. }
  1382.  
  1383.